#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
# Copyright © 2018 crane <crane@crane-pc>
#
# Distributed under terms of the MIT license.



from operator import add
from functools import reduce
from math import log
from time import time


def time_decorator(func):
    def wrapper(*args, **kwargs):
        begin = time()
        result = func(*args, **kwargs)
        span = time() - begin

        print('called [%s] used [%s] seconds' % (func.__name__, span))
        if span > 0.3:
            print('begger then 0.3 second')

        return result

    return wrapper


@time_decorator
def ln2_math():
    return log(2)

def generate_ln2_items(n=10**6):
    '''
    生成n个ln2的泰勒展开项
    '''
    item_ope = lambda x: ((-1) ** (x+1)) * (1/x)
    nums = map(item_ope, range(1,n+1))
    return nums

@time_decorator
def ln2_taylor(n=10**6):
    """
        f=ln(1+x) 根据在x=0处的泰勒展开, 求得f(1)=1 - 1/2 + 1/3 - 1/4 + 1/5 - 1/6....
        也就是ln(2)的值
        因为(1+x)^-1的各阶导数带有阶乘系数, 和泰勒的阶乘系数可以约分
    """
    nums = generate_ln2_items(n)
    sum = reduce(add, nums)

    return sum

def shanks(series):
    series = list(series)
    l = len(series)

    results = []

    # (2, l-1) ===> 索引值(1, l-2)
    for i in range(1, l-1):
        last = series[i-1]
        cur  = series[i]
        next = series[i+1]

        numerator   = (last * next) - (cur * cur)
        denominator = (last + next) - (cur * 2)
        results.append(numerator / denominator)

    # print(series)
    # print(results)
    # print('===================  =====================')
    return results

def get_sum_series(items):
    items = list(items)
    l = len(items)

    # for i in range(l):
    ope = lambda i: sum(items[:i+1])
    sums = map(ope, range(l))
    # assert len(list(sums)) == l
    return sums

@time_decorator
def ln2_taylor_shanks():
    '''
        shanks变换的轮数
        每轮变换结果为上一轮输入的个数减2, 所以如果最后剩下一位, 那么需要初始级数项个数2*times - 1
        比如11, 9, 7, 5, 3, 1
    '''
    shanks_times = 6
    n = shanks_times * 2 - 1
    items = generate_ln2_items(n)
    sums = get_sum_series(items)

    # 循环shanks transform
    input = sums
    for i in range(shanks_times-1):
        output = shanks(input)
        input = output

    return output[0]

def main():
    print("start main")

    l1 = ln2_math()
    print("math   result: [%s]" % l1)

    l2 = ln2_taylor()
    print("Taylor result: [%s]" % l2)

    l3 = ln2_taylor_shanks()
    print("shanks result: [%s]" % l3)

if __name__ == "__main__":
    main()
